#include <stdio.h>
#include <fan-llvm/core/Core.h>


typedef enum MyExceptionType
{
  MyExceptionType_NullErr,
  MyExceptionType_IndexErr
} MyExceptionType;


typedef struct MyException
{
  MyExceptionType type;
  const char* msg;
} MyException;


void functionThatThrowsNullErr(void) {
  printf("[!] Throwing NullErr exception\n");
  MyException* exception = memoryAlloc(sizeof(MyException));
  exception->type = MyExceptionType_NullErr;
  exception->msg = "NullErr exception";
  exceptionThrow(exception);
}


void functionThatThrowsIndexErr(void) {
  printf("[!] Throwing IndexErr exception\n");
  MyException* exception = memoryAlloc(sizeof(MyException));
  exception->type = MyExceptionType_IndexErr;
  exception->msg = "IndexErr exception";
  exceptionThrow(exception);
}


void functionThatShouldNotBeExecuted(void) {
  printf("This message should never get printed to the console!!\n");
}


void testException(void)
{
  ExceptionBlock block = exceptionBegin();
  if (block == ExceptionBlock_Try)
  {
    functionThatThrowsNullErr();
    functionThatShouldNotBeExecuted();
  }
  else if (block == ExceptionBlock_Catch)
  {
    MyException* exception = exceptionCatch();
    printf("** Exception caught: [%d] %s\n", exception->type, exception->msg);
  }
  else if (block == ExceptionBlock_Finally)
  {
    printf("** Finally block executed\n");
    functionThatThrowsIndexErr();
    functionThatShouldNotBeExecuted();
  }
  exceptionEnd();
}


int main()
{
  ExceptionBlock block = exceptionBegin();
  if (block == ExceptionBlock_Try)
  {
    testException();
  }
  else if (block == ExceptionBlock_Catch)
  {
    MyException* exception = exceptionCatch();
    printf("* Exception caught: [%d] %s\n", exception->type, exception->msg);
  }
  else if (block == ExceptionBlock_Finally)
  {
    printf("* Finally block executed\n");
  }
  exceptionEnd();

  return 0;
}
